package tmt.usercenter.web.configure.security.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tmt.usercenter.web.domain.TmtRole;
import tmt.usercenter.web.domain.TmtUser;
import tmt.usercenter.web.domain.TmtUserRole;
import tmt.usercenter.web.domain.security.UserDetailsImpl;
import tmt.usercenter.web.service.TmtUserRoleService;
import tmt.usercenter.web.service.TmtUserService;

import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Transactional
public class TmtServerSessionRegistry extends SessionRegistryImpl {

    //此常量的值不可更改
    public static final String CLIENT_ID = "client_id";

    @Autowired
    private TmtUserRoleService userRoleService;

    private ConcurrentHashMap<HttpSession, Object> tokenSessionMap = new ConcurrentHashMap<>();

    public void addSession(Object token, HttpSession session, Authentication authentication){
        if (session == null)
            return;

        if (tokenSessionMap.keySet().stream().noneMatch(x-> Objects.equals(x.getId(), session.getId()))) {
            tokenSessionMap.put(session, token == null ? "" : token);

            Object clientId = session.getAttribute(CLIENT_ID);
            if (clientId != null) {
                UserDetailsImpl details = (UserDetailsImpl) authentication.getPrincipal();
                details.setClientId((String) clientId);

               List<TmtUserRole> userRoles = userRoleService
                       .findByUserIdWithOriginSystemAndRolesAndAuthorities(details.getUserId(), details.getClientId());

                List<TmtRole> list = userRoles.stream()
                        .filter(x -> Objects.equals(x.getRole().getOriginSystem().getClientId(), clientId))
                        .map(TmtUserRole::getRole)
                        .collect(Collectors.toList());
                //将用户权限切换到ClientId相关的系统上。
                UserDetailsImpl.fillAuthorities(details, list);
            }
        }
    }

    public ConcurrentHashMap<HttpSession, Object> getTokenSessionMap(){
        return tokenSessionMap;
    }

    @Override
    public void removeSessionInformation(String sessionId) {
        try {
            HttpSession session = findSessionById(sessionId);
            if (session != null) {
                tokenSessionMap.remove(session);
                session.invalidate();
            }
        }catch (Exception ex){}
        super.removeSessionInformation(sessionId);
    }

    @Async
    public void invalidateSession(Long userId, String clientId){
        this.getAllPrincipals().stream()
                .filter(x-> Objects.equals(((UserDetailsImpl) x).getUserId(), userId)
                        && (clientId == null || Objects.equals(((UserDetailsImpl) x).getClientId(), clientId) )).forEach(x->{
            UserDetailsImpl details = (UserDetailsImpl)x;
            expireSession(details);
        });
    }

    private void expireSession(UserDetailsImpl details){
        List<SessionInformation> SessionInformations = this.getAllSessions(details, true);
        SessionInformations.forEach(y->{
            y.expireNow();
            removeSessionInformation(y.getSessionId());
            try {
                HttpSession session = this.findSessionById(y.getSessionId());
                SecurityContext sc = (SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT");
                sc.setAuthentication(null);
                System.out.println("---------session expireNow--------------" + y.getSessionId());
            }catch (Exception ex){}
        });
    }

    private HttpSession findSessionById(String sessionId){
        Optional<HttpSession> optional = tokenSessionMap.keySet()
                .parallelStream().filter(x-> Objects.equals(x.getId(), sessionId)).findFirst();
        return optional.orElse(null);
    }

}
